#include "scene.h"
#include "vertexbufferobject.h"
#include "element.h"
#include "factories.h"
#include "textrenderer.h"

// Context implementation

void Context::selectContainer(Container* container)
{
	if (listContainer)
		listContainer->setSelected(false);
	
	if (listContainer == container)
		listContainer = NULL;
	else
		listContainer = container;
	
	if (listContainer)
		listContainer->setSelected(true);
	
	listEnabled = (listContainer != NULL);
}


// Scene implementation

Scene::Scene(DataSource* dataSource)
: timeBased(false), setup(false), dataSource(dataSource)
{
	// Create and set one shared context
	Context* context = new Context();
	eventBasedTrace.setContext(context);
	timeBasedTrace.setContext(context);
}

void Scene::setupForDrawing()
{
    eventBasedTrace.setupForDrawing();
    timeBasedTrace.setupForDrawing();
	emit selectionDescriptionChanged(getSelectionDescription());
	setup = true;
}

void Scene::draw(CameraController* cameraController)
{
	eventBasedTrace.getContext()->cameraController = cameraController;
	timeBasedTrace.getContext()->cameraController = cameraController;
	
	if (!setup)
		setupForDrawing();
	
	Camera& camera = cameraController->getCamera();
	
    const GLubyte* backgroundColor = ColorFactory::instance().getBackgroundColor();
    glClearColor((float)backgroundColor[0] / 255.0f, (float)backgroundColor[1] / 255.0f, (float)backgroundColor[2] / 255.0f, (float)backgroundColor[3] / 255.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    glPushMatrix();
    
    camera.setModelView();
    getTrace().draw(cameraController);
    
    glPopMatrix();
}

void Scene::setDrawMutexesForAll(bool enabled)
{
  eventBasedTrace.setDrawMutexesForAll(enabled);
  timeBasedTrace.setDrawMutexesForAll(enabled);
}

void Scene::createDummy()
{
    // Event based trace
    Thread* twenty = new Thread(2, "twenty");
    eventBasedTrace.addThread(twenty);
    twenty->addState(new State(0, 20));
	twenty->addMutex(new Mutex("takeshi", 25, 42, 0));
	twenty->addMutex(new Mutex("ryu", 26, 32, 1));
	twenty->addMutex(new Mutex("kaneda", 34, 41, 1));
	twenty->addMutex(new Mutex("testuo", 27, 30, 2));
	twenty->addMutex(new Mutex("ryo", 35, 40, 2));
	twenty->addMutex(new Mutex("hideo", 36, 39, 3));
	twenty->addMutex(new Mutex("hiro", 37, 38, 4));
    
    Thread* five = new Thread(5, "five");
    eventBasedTrace.addThread(five);
    five->addState(new State(20, 25));
    
    Thread* oh = new Thread(0, "oh");
    eventBasedTrace.addThread(oh);
    oh->addState(new State(25, 250));
    
    Thread* one = new Thread(1, "one");
    eventBasedTrace.addThread(one);
    one->addState(new State(250, 251));
	
	unsigned pos = 253;
	while (pos < 10000) {
		twenty->addState(new State(pos + 1, pos + 3));
		five->addState(new State(pos - 1, pos + 1));
		oh->addState(new State(pos, pos + 2));
		one->addState(new State(pos + 2, pos + 4));
		pos += 3;
	}
	
    eventBasedTrace.getInterrupts().addState(new State(1, 2));
    eventBasedTrace.getInterrupts().addState(new State(25, 250));
    eventBasedTrace.getInterrupts().addState(new State(256, 512));
    
    // Time based trace
    twenty = new Thread(2, "twenty");
    timeBasedTrace.addThread(twenty);
    twenty->addState(new State(0, 2));
    
    five = new Thread(5, "five");
    timeBasedTrace.addThread(five);
    five->addState(new State(2, 42));
    
    oh = new Thread(0, "oh");
    timeBasedTrace.addThread(oh);
    oh->addState(new State(42, 76));
    
    one = new Thread(1, "one");
    timeBasedTrace.addThread(one);
    one->addState(new State(76, 180));
    
    timeBasedTrace.getInterrupts().addState(new State(0, 4));
    timeBasedTrace.getInterrupts().addState(new State(8, 16));
    timeBasedTrace.getInterrupts().addState(new State(32, 64));
    
    // Events
    unsigned count = 300000;
    while (count--) {
        Event* event = new Event("noclass", "notype");
        eventBasedTrace.addEvent(event);
        timeBasedTrace.addEvent(event);
    }
}

bool Scene::mouseDownWithRay(const Ray& ray, QMouseEvent* event)
{
	if (!setup)
		return false;
	
	bool success = getTrace().mouseDownWithRay(ray, event);
	
	emit selectionDescriptionChanged(getSelectionDescription());
	
	return success;
}

QMap<QString, QString> Scene::getSelectionDescription()
{
	// if event selected -> render all datasource attributes
	// if ui element -> render ui information
	
	QMap<QString, QString> data;
	
	QList<Element*> selection = getTrace().getSelection();
	if (selection.size() == 0) {
		data["selection type"] = "none";
	}
	else if (selection.size() == 1) {
		Element* element = selection.at(0);
		if (element->getType() == Element::EventType) {
			Event* event = (Event*)element;
			data["selection type"] = "event";
			data["event pos"] = QString("%1").arg(event->index);
			data["event class"] = event->getDescription();
			data["event type"] = event->getSecondaryDescription();
			dataSource->fetchDataForEventNr((int)event->index, data);
		}
		else if (element->getType() == Element::StateType) {
			State* state = (State*)element;
			data["selection type"] = "state";
			data["start event pos"] = QString("%1").arg(state->index);
			data["end event pos"] = QString("%1").arg(state->index + state->length);
			data["event length"] = QString("%1").arg(state->length);
			data["state"] = state->getDescription();
		}
		else if (element->getType() == Element::MutexType) {
			Mutex* mutex = (Mutex*)element;
			data["selection type"] = "mutex";
			data["start event pos"] = QString("%1").arg(mutex->index);
			data["end event pos"] = QString("%1").arg(mutex->index + mutex->length);
			data["event length"] = QString("%1").arg(mutex->length);
			data["mutex symbol"] = mutex->getDescription();
		}
	}
	else {
		int totalEvents = 0, totalStates = 0, totalMutexes = 0;
		int minEventNr = -1, maxEventNr = -1;
		QListIterator<Element*> selectionIter(selection);
		while (selectionIter.hasNext()) {
			Element* element = selectionIter.next();
			if (element->getType() == Element::EventType) {
				Event* event = (Event*)element;
				if (event->index < (unsigned)minEventNr || minEventNr < 0)
					minEventNr = event->index;
				if (event->index > (unsigned)maxEventNr || maxEventNr < 0)
					maxEventNr = event->index;
				totalEvents++;
			}
			else if (element->getType() == Element::StateType) {
				State* state = (State*)element;
				if (state->index < (unsigned)minEventNr || minEventNr < 0)
					minEventNr = state->index;
				if (state->index + state->length > (unsigned)maxEventNr || maxEventNr < 0)
					maxEventNr = state->index + state->length;
				totalStates++;
			}
			else if (element->getType() == Element::MutexType) {
				Mutex* mutex = (Mutex*)element;
				if (mutex->index < (unsigned)minEventNr || minEventNr < 0)
					minEventNr = mutex->index;
				if (mutex->index + mutex->length > (unsigned)maxEventNr || maxEventNr < 0)
					maxEventNr = mutex->index + mutex->length;
				totalMutexes++;
			}
		}
		data["selection type"] = "multiple";
		data["total selected"] = QString("%1").arg(selection.size());
		data["total events"] = QString("%1").arg(totalEvents);
		data["total states"] = QString("%1").arg(totalStates);
		data["total mutexes"] = QString("%1").arg(totalMutexes);
		data["min event pos"] = QString("%1").arg(minEventNr);
		data["max event pos"] = QString("%1").arg(maxEventNr);
	}
	
	return data;
}


// Trace implementation

void Trace::setContext(Context* context)
{
	ContextUser::setContext(context);
	interrupts.setContext(context);
	QListIterator<Thread*> threadsIter(threads);
	while (threadsIter.hasNext())
		threadsIter.next()->setContext(context);
}

void Trace::addThread(Thread* thread)
{
	thread->setContext(context);
    threads.append(thread);
}

void Trace::addEvent(Event* event)
{
    events.addElement(event);
}

void Trace::setupForDrawing()
{
    interrupts.setupForDrawing();
    QListIterator<Thread*> threadsIter(threads);
    while (threadsIter.hasNext())
        threadsIter.next()->setupForDrawing();
    setupInitialLayout();
}

void Trace::draw(CameraController* cameraController)
{
	Camera& camera = cameraController->getCamera();
	
    setupLayout(cameraController);
    
    VertexBufferObject::enableClientStates();
	
	// Disable color arrays and unbind the VBOs to enable VAs
    glDisableClientState(GL_COLOR_ARRAY);
    VertexBufferObject::unbindBuffers();
    
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable(GL_BLEND);
	
	// Draw the backgrounds
	interrupts.drawBackgroundWithCamera(camera);
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext())
        threadsIter.next()->drawBackgroundWithCamera(camera);
	
	glDisable(GL_BLEND);
	
    VertexBufferObject::enableClientStates();

    // Draw the VBOs
    grid.drawVBOWithCamera(camera);
    threadsIter.toFront();
    while (threadsIter.hasNext())
        threadsIter.next()->drawVBOWithCamera(camera);
	
	if (context->drawInterrupts())
		interrupts.drawVBOWithCamera(camera);
	if (context->drawEvents())
		events.drawVBOWithCamera(camera);
	
	if (context->drawList()) {
		glEnable(GL_BLEND);
		camera.disableClipPlanes();
		list.drawVBOWithCamera(camera);
		camera.enableClipPlanes();
		glDisable(GL_BLEND);
	}
    
    
	// Disable color arrays and unbind the VBOs to enable VAs
    glDisableClientState(GL_COLOR_ARRAY);
    VertexBufferObject::unbindBuffers();
    
    // Draw the VAs
    threadsIter.toFront();
    while (threadsIter.hasNext())
        threadsIter.next()->drawVAWithCamera(camera);
	
	if (context->drawInterrupts())
		interrupts.drawVAWithCamera(camera);
	if (context->drawEvents())
		events.drawVAWithCamera(camera);
	
	if (context->drawList()) {
		camera.disableClipPlanes();
		list.drawVAWithCamera(camera);
		camera.enableClipPlanes();
	}
	
	grid.drawVAWithCamera(camera);
    
    VertexBufferObject::disableClientStates();
    
    
	// Draw the text
    TextRenderer::instance().begin();
	threadsIter.toFront();
    while (threadsIter.hasNext())
        threadsIter.next()->drawTextWithCamera(camera);
	
	if (context->drawInterrupts())
		interrupts.drawTextWithCamera(camera);
	if (context->drawEvents())
		events.drawTextWithCamera(camera);
	
	if (context->drawList()) {
		camera.disableClipPlanes();
		list.drawTextWithCamera(camera);
		camera.enableClipPlanes();
	}
	grid.drawTextWithCamera(camera);
    TextRenderer::instance().end();
}

void Trace::setupInitialLayout()
{
    orderedThreads = threads;
	float maxLength = 0.0f;
	QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        float length = threadsIter.next()->getLength();
		if (length > maxLength)
			maxLength = length;
	}
	
	threadsIter.toFront();
    while (threadsIter.hasNext())
        threadsIter.next()->setLength(maxLength);
}

void Trace::setupScaling(CameraController* cameraController)
{
	Vec3f scale;
	cameraController->getScaling(scale);
	
    QListIterator<Thread*> threadsIter(threads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
        thread->setScaling(scale);
    }
	
	events.setScaling(scale);
	interrupts.setScaling(scale);
	grid.setUnitLength(scale[0]);
}

void Trace::setupLayout(CameraController* cameraController)
{
	Camera& camera = cameraController->getCamera();
	
	context->cameraNoPitch = camera.noPitch();
	context->cameraNoYaw = camera.noYaw();
	
	setupScaling(cameraController);
	
	camera.clearClipPlanes();
	if (context->drawList()) {
		// Get the camera clip plane positions
		list.setupLayoutWithCamera(camera);
		float left = list.getLeftWidth();
		float right = list.getRightWidth();
		
		// Setup the camera clip planes
		glPushMatrix();
		glLoadIdentity();
		camera.setLeftClipPlane(left);
		camera.setRightClipPlane(right);
		camera.enableClipPlanes();
		glPopMatrix();
		
		// Setup the list contents
		bool leftHitsFirst = false, rightHitsFirst = false;
		list.setLeftList(context->listContainer->leftClippedElements(camera, list.getMaxListSize(), leftHitsFirst));
		list.setRightList(context->listContainer->rightClippedElements(camera, list.getMaxListSize(), rightHitsFirst));
		list.setLeftHitsFirst(leftHitsFirst);
		list.setRightHitsFirst(rightHitsFirst);
	}
	
    // Thread layout
    float threadPadding = 0.5;
    
    // Let's see how much space is required for the threads
    float totalThreadHeight = (float)(orderedThreads.size() - 1) * threadPadding;
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) 
    {
        Thread* thread = threadsIter.next();
        thread->layoutWithCamera(camera);
        float threadheight = thread->labeledBoundingBox().height();
        if (threadheight == 0)
        {
          // if this thread doesn't take up any space, remove the corresponding padding (also see below)
          threadheight = -threadPadding;
        }
        totalThreadHeight += threadheight;
    }
    
    // Now we can position the threads in the space
    Vec3f threadPosition = {0, totalThreadHeight / 2.0, 0};
    threadsIter.toFront();
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
        threadPosition[1] -= thread->labeledBoundingBox().max[1];
        thread->setPosition(threadPosition);
        threadPosition[1] -= -thread->labeledBoundingBox().min[1];
        if (thread->labeledBoundingBox().height() > 0)
        {
          // only add padding if the thread itself takes up any space (also see above)
          threadPosition[1] -= threadPadding;
        }
    }
    
    // Events layout
    float eventsBottomPadding = 0.0;
    
    Vec3f eventsPosition;
	if (context->screenSpaceLayout()) {
		Vec3f normBottomStart = {0, -1, 1}; 
		Vec3f normBottomEnd = {0, -1, -1};
		camera.getNormalizedSpacePickRay(normBottomStart, normBottomEnd).getPoint(eventsPosition, 0);
		eventsPosition[0] = 0;
		eventsPosition[1] += -events.boundingBox.min[1] + eventsBottomPadding;
		eventsPosition[2] = 0;
	}
	else {
		SetVec3f(eventsPosition, threadPosition);
		eventsPosition[1] -= events.boundingBox.max[1];
	}
    events.setPosition(eventsPosition);
    
    
    // Interrrupts layout
    float interruptsTopPadding = 0.0;
    
	interrupts.layoutWithCamera(camera);
    Vec3f interruptsPosition;
	if (context->screenSpaceLayout()) {
		Vec3f normTopStart = {0, 1, 1}; 
		Vec3f normTopEnd = {0, 1, -1};
		camera.getNormalizedSpacePickRay(normTopStart, normTopEnd).getPoint(interruptsPosition, 0);
		interruptsPosition[0] = 0;
		interruptsPosition[1] -= interrupts.labeledBoundingBox().max[1] + interruptsTopPadding;
		interruptsPosition[2] = 0;
	}
	else {
		SetVec3f(interruptsPosition, 0, totalThreadHeight / 2.0 + threadPadding - interrupts.labeledBoundingBox().min[1], 0);// - interrupts.labeledBoundingBox().min[1], 0.0);
	}
    interrupts.setPosition(interruptsPosition);
	
	
	// Grid layout
	float gridBottomPadding = 1.5f;
	
    Vec3f gridPosition;
    Vec3f gridBottomStart = {0, -1, 1}; 
    Vec3f gridBottomEnd = {0, -1, -1};
    camera.getNormalizedSpacePickRay(gridBottomStart, gridBottomEnd).getPoint(gridPosition, 0);
	grid.setHorizontalPosition(gridPosition[1] + gridBottomPadding);
}

bool Trace::mouseDownWithRay(const Ray& ray, QMouseEvent* event)
{
	if (context->hitList() && list.hit(ray)) {
		Element* listElement = list.pickElementWithRay(ray);
		if (listElement) {
			qDebug() << "listElement hitted";
			Vec3f elementCenter;
			listElement->boundingBox.getCenter(elementCenter);
			Vec3f destination;
			context->cameraController->getCamera().getPosition(destination);
			destination[0] = elementCenter[0];
			context->cameraController->moveToTrace(destination);
		}
		return true;
	}
	
	if (context->hitEvents() && events.mouseDownWithRay(ray, event))
		return true;
	
	if (context->hitInterrupts() && interrupts.mouseDownWithRay(ray, event))
		return true;;
	
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
		if (thread->mouseDownWithRay(ray, event))
			return true;
    }
	
	return true;
}

QList<Element*> Trace::getSelection() const
{
	QList<Element*> selection;
	selection << interrupts.getSelection();
	selection << events.getSelection();
	
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
		selection << thread->getSelection();
    }
	
	return selection;
}

BoundingBox Trace::getSelectionBoundingBox() const
{
	BoundingBox mergedBB;
	bool first = true;
	
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
		if (thread->getSelection().size() > 0) {
			BoundingBox worldBoundingBox = thread->getSelectionBoundingBox();
			if (first) {
				mergedBB = worldBoundingBox;
				first = false;
			}
			else {
				mergedBB.merge(worldBoundingBox);
			}
		}
    }
	
	return mergedBB;
}

void Trace::updateColors()
{
	interrupts.updateColors();
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
		thread->updateColors();
    }
	events.reassignColors();
}

void Trace::dummySearch(QString state)
{
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
		thread->dummySearch(state);
    }
	updateColors();
}

void Trace::clearSearch()
{
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
		thread->clearSearch();
    }
	updateColors();
}

bool Trace::clearSelection()
{
	bool cleared = false;
	
	cleared |= interrupts.clearSelection();
	cleared |= events.clearSelection();
	
    QListIterator<Thread*> threadsIter(orderedThreads);
    while (threadsIter.hasNext()) {
        Thread* thread = threadsIter.next();
		cleared |= thread->clearSelection();
    }
	
	return cleared;
}

void Trace::setDrawMutexesForAll(bool enabled)
{
  QListIterator<Thread*> threadsIter(orderedThreads);
  while(threadsIter.hasNext())
  {
    Thread* thread = threadsIter.next();
    thread->setDrawMutexes(enabled);
  }
}

// Events implementation

bool Events::mouseDownWithRay(const Ray& ray, QMouseEvent* event)
{
    QVector<Element*> eventsPicks = pickElementsWithRay(ray);
    if (eventsPicks.size() > 0)
        toggleElementSelected(eventsPicks.at(0));
	
	return (eventsPicks.size() > 0);
}


// Thread implementation

Thread::Thread(int id, const QString& name)
: id(id), name(name), drawMutexes(false)
{
	states.setName(name);
	states.setLabeled(!drawMutexes);
	mutexes.setName(name);
	mutexes.setLabeled(drawMutexes);
	mutexes.boundingBox.min[1] = -0.5;
}

void Thread::addState(State* state)
{
//    qDebug() << "Thread::addState() : to " << states.getName() << " add " << state->description << " from " << state->index << " to " << state->index + state->length;
    states.addElement(state);
}

void Thread::addMutex(Mutex* mutex)
{
	mutexes.addElement(mutex);
}

void Thread::setPosition(Vec3f position)
{
	if (!drawMutexes) {
		Vec3f statesScaling;
		mutexes.getScaling(statesScaling);
		states.setScaling(statesScaling);
		states.setPosition(position);
		return;
	}
	
	mutexes.setPosition(position);
	
	Vec3f statesPosition;
	Vec3f offset = {0.0f, mutexes.boundingBox.height() / 2.0f - 0.5f, 0.0f};
	AddVec3f(statesPosition, position, offset);
	states.setPosition(statesPosition);
	
	Vec3f statesScaling;
	mutexes.getScaling(statesScaling);
	statesScaling[1] *= mutexes.boundingBox.height();
	states.setScaling(statesScaling);
}

void Thread::setupForDrawing()
{
    states.compile();
    states.setupForDrawing();
	
	mutexes.compile();
	mutexes.setupForDrawing();
}

void Thread::drawVBOWithCamera(Camera& camera)
{
    states.drawVBOWithCamera(camera);
	
	if (drawMutexes)
		mutexes.drawVBOWithCamera(camera);
}

void Thread::drawBackgroundWithCamera(Camera& camera)
{
	if (drawMutexes)
		mutexes.drawBackgroundWithCamera(camera);
	else
		states.drawBackgroundWithCamera(camera);
}

void Thread::drawVAWithCamera(Camera& camera)
{
    states.drawVAWithCamera(camera);
	
	if (drawMutexes)
		mutexes.drawVAWithCamera(camera);
}

void Thread::drawTextWithCamera(Camera& camera)
{
    states.drawTextWithCamera(camera);
	
	if (drawMutexes)
		mutexes.drawTextWithCamera(camera);
}

bool Thread::mouseDownWithRay(const Ray& ray, QMouseEvent* event)
{
	if ((!drawMutexes && !states.hit(ray)) || (drawMutexes && !mutexes.hit(ray)))
		return false;
	
	if (event->button() == Qt::RightButton)
	{
		return setDrawMutexes(!drawMutexes);
	}
	
	QVector<Element*> pickedElements = states.pickElementsWithRay(ray);
	if (pickedElements.size() > 0)
		toggleElementSelected(pickedElements.first());
	else
		context->selectContainer(&states);
	
	return true;
}

void Thread::dummySearch(QString state)
{
	QVectorIterator<Element*> stateIter(states.getElements());
	while (stateIter.hasNext()) {
		Element* elementState = stateIter.next();
		if (elementState->getDescription() == state) {
			elementState->setDimmed(false);
			states.setElementSelected(elementState, true);
		}
		else {
			elementState->setDimmed(true);
			states.setElementSelected(elementState, false);
		}
	}
}

void Thread::clearSearch()
{
	QVectorIterator<Element*> stateIter(states.getElements());
	while (stateIter.hasNext()) {
		Element* state = stateIter.next();
		state->setDimmed(false);
	}
}

QList<Element*> Thread::getSelection() const
{
	QList<Element*> selection;
	selection << states.getSelection();
	
	if (drawMutexes)
		selection << mutexes.getSelection();
	
	return selection;
}

BoundingBox Thread::getSelectionBoundingBox() const
{
	bool first = true;
	BoundingBox mergedBB;
	QListIterator<Element*> statesIter(states.getSelection());
	while (statesIter.hasNext()) {
		Element* selectedState = statesIter.next();
		BoundingBox bb = selectedState->boundingBox;
		
		Vec3f scaling;
		states.getScaling(scaling);
		bb.scale(scaling);
		
		Vec3f translation;
		states.getPosition(translation);
		bb.translate(translation);
		
		if (first) {
			mergedBB = bb;
			first = false;
		}
		else {
			mergedBB.merge(bb);
		}
	}
	
	QListIterator<Element*> mutexesIter(mutexes.getSelection());
	while (mutexesIter.hasNext()) {
		Element* selectedMutex = mutexesIter.next();
		BoundingBox bb = selectedMutex->boundingBox;
		
		Vec3f scaling;
		mutexes.getScaling(scaling);
		bb.scale(scaling);
		
		Vec3f translation;
		mutexes.getPosition(translation);
		bb.translate(translation);
		
		if (first) {
			mergedBB = bb;
			first = false;
		}
		else {
			mergedBB.merge(bb);
		}
	}
	
	return mergedBB;
}

bool Thread::setDrawMutexes(bool enabled)
{
  if (mutexes.getElements().size() > 0) 
  {
    drawMutexes = enabled;
    states.setLabeled(!drawMutexes);
    mutexes.setLabeled(drawMutexes);
    return true;
  }
  else
  {
    return false;
  }
}

// Interrupts implementation

void Interrupts::addState(State* state)
{
    states.addElement(state);
}

void Interrupts::setupForDrawing()
{
    states.compile();
    states.setupForDrawing();
}

void Interrupts::drawVBOWithCamera(Camera& camera)
{
    states.drawVBOWithCamera(camera);
}

void Interrupts::drawBackgroundWithCamera(Camera& camera)
{
	states.drawBackgroundWithCamera(camera);
}

void Interrupts::drawVAWithCamera(Camera& camera)
{
    states.drawVAWithCamera(camera);
}

void Interrupts::drawTextWithCamera(Camera& camera)
{
    states.drawTextWithCamera(camera);
}

bool Interrupts::mouseDownWithRay(const Ray& ray, QMouseEvent* event)
{
	QVector<Element*> pickedElements = states.pickElementsWithRay(ray);
	if (pickedElements.size() > 0)
		toggleElementSelected(pickedElements.first());
	
	return (pickedElements.size() > 0);
}
